﻿using System;

using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace gpsmid_wince.util {
	public class Entity {
		public sbyte type;
	}

	class Way : Entity {
		public const sbyte WAY_FLAG_NAME = 1;
		public const sbyte WAY_FLAG_MAXSPEED = 2;
		public const sbyte WAY_FLAG_LAYER = 4;
		public const sbyte WAY_FLAG_RESERVED_FLAG = 8;
		public const sbyte WAY_FLAG_ONEWAY = 16;
		//	public const sbyte WAY_FLAG_MULTIPATH = 4;	
		public const sbyte WAY_FLAG_NAMEHIGH = 32;
		public const sbyte WAY_FLAG_AREA = 64;
		//	public const sbyte WAY_FLAG_ISINHIGH = 64;	
		public const int WAY_FLAG_ADDITIONALFLAG = 128;

		public const sbyte WAY_FLAG2_ROUNDABOUT = 1;
		public const sbyte WAY_FLAG2_TUNNEL = 2;
		public const sbyte WAY_FLAG2_BRIDGE = 4;
		public const sbyte WAY_FLAG2_CYCLE_OPPOSITE = 8;
		public const sbyte WAY_FLAG2_LONGWAY = 16;
		public const sbyte WAY_FLAG2_MAXSPEED_WINTER = 32;
		/** http://wiki.openstreetmap.org/wiki/WikiProject_Haiti */
		public const sbyte WAY_FLAG2_DAMAGED = 64;
		public const int WAY_FLAG2_ADDITIONALFLAG = 128;

		public const sbyte WAY_FLAG3_URL = 1;
		public const sbyte WAY_FLAG3_URLHIGH = 2;
		public const sbyte WAY_FLAG3_PHONE = 4;
		public const sbyte WAY_FLAG3_PHONEHIGH = 8;
		public const sbyte WAY_FLAG3_NAMEASFORAREA = 16;
		public const sbyte WAY_FLAG3_HAS_HOUSENUMBERS = 32;
		public const sbyte WAY_FLAG3_LONGHOUSENUMBERS = 64;

		public const sbyte DRAW_BORDER = 1;
		public const sbyte DRAW_AREA = 2;
		public const sbyte DRAW_FULL = 3;

		private const int MaxSpeedMask = 0xff;
		private const int MaxSpeedShift = 0;
		//private const int ModMask = 0xff00;
		private const int ModShift = 8;

		public const int WAY_ONEWAY = 1 << ModShift;
		public const int WAY_AREA = 2 << ModShift;
		public const int WAY_ROUNDABOUT = 4 << ModShift;
		public const int WAY_TUNNEL = 8 << ModShift;
		public const int WAY_BRIDGE = 16 << ModShift;
		public const int WAY_CYCLE_OPPOSITE = 32 << ModShift;
		/** http://wiki.openstreetmap.org/wiki/WikiProject_Haiti */
		public const int WAY_DAMAGED = 64 << ModShift;
		public const int WAY_NAMEASFORAREA = 128 << ModShift;

		public const sbyte PAINTMODE_COUNTFITTINGCHARS = 0;
		public const sbyte PAINTMODE_DRAWCHARS = 1;
		public const sbyte INDENT_PATHNAME = 2;

		private const int PATHSEG_DO_NOT_HIGHLIGHT = -1;
		private const int PATHSEG_DO_NOT_DRAW = -2;

		private const int HIGHLIGHT_NONE = 0;
		private const int HIGHLIGHT_DEST = 1;
		private const int HIGHLIGHT_ROUTEPATH_CONTAINED = 2;

		protected static Logger logger = Logger.getInstance("Way", Logger.DEBUG);
		public int flags = 0;
		private int flagswinter = 0;

		/** indicate by which route modes this way can be used (motorcar, bicycle, etc.)
		 * Each bit represents one of the route modes in the midlet, with bit 0 being the first route mode.
		 * The routeability of the way in each route mode is determined by Osm2GpsMid,
		 * which derives it from the accessible flag of the wValue of way type's description
		 * and the routeAccess specifications in the style file 
		 * Higher bits (bit 4 to 7 are used for the same flags like in Connection)
		 */
		private sbyte wayRouteModes = 0;

		public short[] path;
		public short minLat;
		public short minLon;
		public short maxLat;
		public short maxLon;

		public short wayNrInFile = 0;
		public int nameIdx = -1;
		public int urlIdx = -1;
		public int phoneIdx = -1;


		public Way(JavaDataInputStream is_, sbyte f, Tile t, sbyte[] layers, int idx) {

			minLat = is_.readShort();
			minLon = is_.readShort();
			maxLat = is_.readShort();
			maxLon = is_.readShort();

			type = is_.readByte();
			setWayRouteModes(is_.readByte());

			if ((f & WAY_FLAG_NAME) == WAY_FLAG_NAME) {
				if ((f & WAY_FLAG_NAMEHIGH) == WAY_FLAG_NAMEHIGH) {
					nameIdx = is_.readInt();
					//System.out.println("Name_High " + f );
				} else {
					nameIdx = is_.readShort();
				}
			}
			if ((f & WAY_FLAG_MAXSPEED) == WAY_FLAG_MAXSPEED) {
				//			logger.debug("read maxspeed");
				flags = is_.readByte() & 0xff; // apply an 8 bit mask to the maxspeed sbyte read so values >127 won't result in a negative integer with wrong bits set for the flags
			}

			sbyte f2 = 0;
			sbyte f3 = 0;
			if ((f & WAY_FLAG_ADDITIONALFLAG) > 0) {
				f2 = is_.readByte();
				if ((f2 & WAY_FLAG2_ROUNDABOUT) > 0) {
					flags += WAY_ROUNDABOUT;
				}
				if ((f2 & WAY_FLAG2_TUNNEL) > 0) {
					flags += WAY_TUNNEL;
				}
				if ((f2 & WAY_FLAG2_BRIDGE) > 0) {
					flags += WAY_BRIDGE;
				}
				if ((f2 & WAY_FLAG2_DAMAGED) > 0) {
					flags += WAY_DAMAGED;
				}
				if ((f2 & WAY_FLAG2_CYCLE_OPPOSITE) > 0) {
					flags += WAY_CYCLE_OPPOSITE;
				}
				if ((f2 & WAY_FLAG2_ADDITIONALFLAG) == WAY_FLAG2_ADDITIONALFLAG) {
					f3 = is_.readByte();
					if ((f3 & WAY_FLAG3_NAMEASFORAREA) > 0) {
						flags += WAY_NAMEASFORAREA;
					}

					if ((f3 & WAY_FLAG3_URL) > 0) {
						if ((f3 & WAY_FLAG3_URLHIGH) > 0) {
							urlIdx = is_.readInt();
						} else {
							urlIdx = is_.readShort();

						}
					}
					if ((f3 & WAY_FLAG3_PHONE) > 0) {
						if ((f3 & WAY_FLAG3_PHONEHIGH) > 0) {
							phoneIdx = is_.readInt();
						} else {
							phoneIdx = is_.readShort();

						}
					}
					if ((f3 & WAY_FLAG3_HAS_HOUSENUMBERS) > 0) {
						int hcount;
						// Ignore the data for now
						if ((f3 & WAY_FLAG3_LONGHOUSENUMBERS) > 0) {
							hcount = is_.readShort();
							if (hcount < 0) {
								hcount += 65536;
							}
						} else {
							hcount = is_.readByte();
							if (hcount < 0) {
								hcount += 256;
							}
						}
						logger.debug("expecting " + hcount + " housenumber nodes");
						for (short i = 0; i < hcount; i++) {
							is_.readLong();
						}
					}
				}

			}

			if ((f2 & WAY_FLAG2_MAXSPEED_WINTER) == WAY_FLAG2_MAXSPEED_WINTER) {
				setFlagswinter(is_.readByte());
			}

			layers[idx] = 0;
			if ((f & WAY_FLAG_LAYER) == WAY_FLAG_LAYER) {
				/**
				 * TODO: We are currently ignoring the layer info
				 * Please implement proper support for this when rendering
				 */
				layers[idx] = is_.readByte();
			}
			if ((f & WAY_FLAG_ONEWAY) == WAY_FLAG_ONEWAY) {
				flags += WAY_ONEWAY;
			}
			if ((f & WAY_FLAG_AREA) > 0) {
				flags += WAY_AREA;
				logger.debug("Area = true");
			}

			bool longWays = false;
			if ((f2 & WAY_FLAG2_LONGWAY) > 0) {
				longWays = true;
				logger.debug("longway = true");
			}
			int count;
			if (longWays) {
				count = is_.readShort();
				if (count < 0) {
					count += 65536;
				}
			} else {
				count = is_.readByte();
				if (count < 0) {
					count += 256;
				}
			}
			path = new short[count];
			logger.trace("expecting " + count + " nodes");
			for (short i = 0; i < count; i++) {
				path[i] = is_.readShort();
			}
		}


		public void setWayRouteModes(sbyte wayRouteModes) {
			this.wayRouteModes = wayRouteModes;
		}

		public void setFlagswinter(int flagswinter) {
			this.flagswinter = flagswinter;
		}

		private void circleWayEnd (PaintContext pc, int x, int y, int radius) {
			pc.g.FillEllipse(pc.br, x - radius, y - radius, radius*2, radius*2);
		}

		public static int countDrawn;
		/* <summary> paints this path </summary> */
		public void processPath(PaintContext pc, SingleTile t, int mode, sbyte layer) {
			Point[] pts = new Point[path.Length];
			Pen cPen = new Pen(t.getColor() );
			SolidBrush cBrush = new SolidBrush(t.getColor() );
			countDrawn ++;
			for(int i=0; i<path.Length; i++) {

				short id = path[i];
				float lt = t.getAbsLat(t.nodeLat[id]);
				float ln = t.getAbsLon(t.nodeLon[id]);

				Point p = pc.p.toWin( lt, ln );
				//logger.debug("rendering point "+p);
				pts[i] = p;
			}
			bool isarea;
			WayDescription desc = Legend.getWayDescription(type);
			isarea = isArea() || desc.isArea || desc.isBuilding(); 
			if ( isarea ) {
				//logger.debug("drawing way as area!!");
				cBrush.Color = Color.FromArgb( (int)(0xFF000000 | desc.lineColor) );
				pc.g.FillPolygon(cBrush, pts); 
			}
			pc.g.DrawLines(cPen, pts);
		}

		public bool isOnScreen( PaintContext pc, SingleTile t) {
			return pc.p.isOnScreen(t.getAbsLat(minLat), t.getAbsLon(minLon),
				t.getAbsLat(maxLat), t.getAbsLon(maxLon) );
		}
		public bool isOnScreen( short pcLDlat, short pcLDlon, short pcRUlat, short pcRUlon) { 
			if ((maxLat < pcLDlat) || (minLat > pcRUlat)) return false;
			if ((maxLon < pcLDlon) || (minLon > pcRUlon)) return false;
			return true; 
		}

		public bool isArea() {return (flags & WAY_AREA) != 0; }
	}
}
